package com.cjd.common.utils

import android.content.Context
import android.text.TextUtils
import com.cjd.common.bean.BaseBean
import com.cjd.common.event.HttpRequestEvent
import com.cjd.common.okhttp3.OkHttp3Callback
import com.cjd.common.okhttp3.RequestParamsContext
import com.cjd.common.preferences.PreferencesHelper
import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.io.IOException
import java.net.HttpURLConnection
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit


/**
 * @Author chenjidong
 * @email 374122600@qq.com
 * created 2019/7/4
 * description okhttp3 封装
 */
object OkHttpUtils {
    private var client: OkHttpClient? = null
    private const val CONNECT_TIME_OUE = 10
    private const val WRITE_TIME_OUT = 15
    private const val READ_TIME_OUT = 10
    var CACHE_DIRECTORY = "_http_cache"
    private const val CACHE_SIZE = 32 * 1024 * 1024

    const val AUTH = "Authorization"
    const val USER_AGENT = "User-Agent"
    const val CACHE_CONTROL = "Cache-Control"//HTTP 1.1
    const val CACHE_CONTROL_ONLY_CACHED = "public, only-if-cached, max-stale=2419200"
    const val CACHE_CONTROL_NETWORK = "public,max-age=0"
    const val PRAGMA = "Pragma"//HTTP 1.0+

    fun getClient(): OkHttpClient? {
        if (client == null) throw RuntimeException("Please on application init()!")
        return client
    }

    fun init(
        context: Context,
        encryptType: String? = null,
        encryptKey: String? = null,
        block: ((builder: OkHttpClient.Builder) -> Unit)? = null
    ) {
        if (client == null) {
            synchronized(OkHttpUtils::class) {
                if (client == null) {
                    val dispatcher = Dispatcher(Executors.newFixedThreadPool(20))
                    dispatcher.maxRequests = 20
                    dispatcher.maxRequestsPerHost = 1
                    CACHE_DIRECTORY = context.cacheDir.absolutePath + File.separator + "_http_cache"

                    val cacheFile = File(CACHE_DIRECTORY)
                    val cache = Cache(cacheFile, CACHE_SIZE.toLong())
                    val builder = OkHttpClient.Builder()

                    if (!TextUtils.isEmpty(encryptType)) {
                        builder.addInterceptor(EncryptInterceptor(context, encryptType, encryptKey))
                    }

                    builder.dispatcher(dispatcher)
                        .cache(cache).connectTimeout(CONNECT_TIME_OUE.toLong(), TimeUnit.SECONDS)
                        .writeTimeout(WRITE_TIME_OUT.toLong(), TimeUnit.SECONDS)
                        .readTimeout(READ_TIME_OUT.toLong(), TimeUnit.SECONDS)
                        .connectionPool(ConnectionPool(100, 30, TimeUnit.SECONDS))
                        .addNetworkInterceptor(AuthInterceptor(context))

                    block?.invoke(builder)

                    client = builder.build()
                }
            }
        }
    }


    /**
     * 同步请求 （Coroutine 方式） 默认 get
     * @param method get / post
     * @param url 请求url
     * @param params 请求参数
     * @param clazz 回调实体类型
     * @return T:BaseBean code != 0 可直接显示 msg
     */
    suspend fun <T : BaseBean> syncRequest(
        method: String,
        url: String,
        params: Map<String, Any>,
        clazz: Class<T>
    ): T {
        val builder = Request.Builder()
        val request = if ("POST" == method || "post" == method) {
            builder.url(url).post(getPostParams(params)).build()
        } else {
            val paramsStr = getGetParams(params)
            val finalUrl = if (TextUtils.isEmpty(paramsStr)) url else {
                if (url.contains("?")) "$url&$paramsStr" else "$url?$paramsStr"
            }
            builder.url(finalUrl).build()
        }
        try {
            val response = getClient()?.newCall(request)?.execute()
            return if (response?.code() == HttpURLConnection.HTTP_OK) {
                val json = response.body()?.string()
                LogUtils.d("请求：" + request.url().toString() + " 结果： " + json)
                GsonUtils.instance().fromJson(json, clazz)
            } else {
                clazz.newInstance().apply {
                    this.code = response?.code() ?: -1
                    this.msg = "系统繁忙！"
                }
            }
        } catch (e: Exception) {
            LogUtils.e(e)
            return clazz.newInstance().apply {
                this.code = 500
                this.msg = "系统错误！"
            }
        }
    }

    /**
     * 异步 请求
     * @param method get / post
     * @param url 请求url
     * @param params 请求参数
     * @param callback OkHttp3 回调
     */
    fun <T : Callback> asyncRequest(
        method: String,
        url: String,
        params: Map<String, Any>,
        callback: T
    ) {
        val builder = Request.Builder()
        val request = if ("POST" == method || "post" == method) {
            builder.url(url).post(getPostParams(params)).build()
        } else {
            val paramsStr = getGetParams(params)
            val finalUrl = if (TextUtils.isEmpty(paramsStr)) url else {
                if (url.contains("?")) "$url&$paramsStr" else "$url?$paramsStr"
            }
            builder.url(finalUrl).build()
        }
        getClient()?.newCall(request)?.enqueue(callback)
    }

    /**
     * okHttp3 get 请求
     * @param url 请求url
     * @param params 请求参数
     * @param callback 请求回调
     */
    fun <T : BaseBean> httpGet(
        url: String,
        params: Map<String, Any>,
        callback: OkHttp3Callback<T>
    ) {
        asyncRequest("GET", url, params, callback)
    }

    /**
     * okHttp3 post 请求
     * @param url 请求url
     * @param params 请求参数
     * @param callback 请求回调
     */
    fun <T : BaseBean> httpPost(
        url: String,
        params: Map<String, String>,
        callback: OkHttp3Callback<T>
    ) {
        asyncRequest("POST", url, params, callback)
    }

    /**
     * 转换 get 请求参数
     * @param params 参数 字典
     * @return String 例："a=1&b=2"
     */
    fun getGetParams(params: Map<String, Any>): String {
        val stringBuilder = StringBuilder()
        params.forEach {
            stringBuilder.append(it.key)
            stringBuilder.append("=")
            stringBuilder.append(it.value)
            stringBuilder.append("&")
        }
        return stringBuilder.toString()
    }

    /**
     * 转换post 请求 body
     * @param params 请求参数
     * @return FormBody 默认 "application/x-www-form-urlencoded"
     */
    fun getPostParams(params: Map<String, Any>): FormBody {
        val builder = FormBody.Builder()
        params.forEach {
            builder.add(it.key, it.value.toString())
        }
        return builder.build()
    }


    private class EncryptInterceptor(
        private val context: Context,
        val encryptType: String? = null,
        val encryptKey: String? = null
    ) :
        Interceptor {


        override fun intercept(chain: Interceptor.Chain): Response {
            var request = chain.request()
            request = RequestParamsContext(context, request, encryptType, encryptKey).getInRequest()
            return chain.proceed(request)
        }

    }


    private class AuthInterceptor(private val context: Context) : Interceptor {
        private var request: Request? = null
        private var builder: Request.Builder? = null
        private var auth: String? = null

        @Throws(IOException::class)
        override fun intercept(chain: Interceptor.Chain): Response {
            request = chain.request()

            if (TextUtils.isEmpty(auth)) {
                auth = PreferencesHelper.getString(context, AUTH, "")
            }
            if (!TextUtils.isEmpty(auth)) {
                builder = request?.newBuilder()
                builder?.addHeader(AUTH, "bearer $auth")
                request = builder?.build()
            }

            val response = chain.proceed(request!!)
            if (response.code() != HttpURLConnection.HTTP_OK)
                EventBus.getDefault().post(HttpRequestEvent(response.code()))

            return response
        }
    }

    private class HttpLogger : HttpLoggingInterceptor.Logger {
        override fun log(message: String) {
            if (!TextUtils.isEmpty(message)) {
                if (message.contains("http") || GsonUtils.isJson(message)) {
                    LogUtils.d(message)
                }
            }
        }
    }

    private class LogInterceptor : Interceptor {

        override fun intercept(chain: Interceptor.Chain): Response {
            val response = chain.proceed(chain.request())
            if (response.code() != HttpURLConnection.HTTP_OK) {
                EventBus.getDefault().post(HttpRequestEvent(response.code()))
            }
            LogUtils.d(
                "--->request:" + response.request()
                    .method() + ":" + response.code() + ":" + response.request().url()
            )
            return response
        }
    }

    private class RequestCacheInterceptor(val context: Context) : Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            var request = chain.request()
            if (!NetUtils.hasNetwork(context)) {
                request = request.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, only-if-cached")
                    .build()
            }
            return chain.proceed(request)
        }

    }

    private class ResponseCacheInterceptor : Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            val originalResponse = chain.proceed(chain.request())
            val cacheControl = originalResponse.header("Cache-Control")
            return if (cacheControl == null || cacheControl.contains("no-store") || cacheControl.contains(
                    "no-cache"
                ) ||
                cacheControl.contains("must-revalidate") || cacheControl.contains("max-age=0")
            ) {
                originalResponse.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, max-age=" + 5000)
                    .build()
            } else
                originalResponse
        }

    }

}